package com.qp.bookkeeping.common.wx.service.impl;

import cn.hutool.core.map.MapUtil;
import cn.hutool.json.JSONUtil;
import com.google.common.collect.Lists;
import com.qp.bookkeeping.business.book.mapper.UserMapper;
import com.qp.bookkeeping.business.book.model.Do.UserDo;
import com.qp.bookkeeping.business.book.model.Dto.UserDto;
import com.qp.bookkeeping.business.book.model.req.UpdateUserReq;
import com.qp.bookkeeping.business.common.Exception.BusinessException;
import com.qp.bookkeeping.common.model.req.WxLoginReq;
import com.qp.bookkeeping.common.model.rsp.BusinessExceptionEnum;
import com.qp.bookkeeping.common.wx.service.WxService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

/**
 * @author xutianqi
 */
@Service
@Slf4j
public class WxServiceImpl implements WxService {
    @Value("${wx.userInfoUrl}")
    String requestUrl;

    @Value("${wx.appId}")
    String appId;

    @Value("${wx.appSecret}")
    String appSecret;

    @Autowired
    UserMapper userMapper;

    /**
     * 获取微信用户信息
     *
     * @param req 授权码
     * @return 微信用户信息
     */
    @Override
    public UserDo loginWithCode(WxLoginReq req) {
        String phoneNumber = req.getPhoneNumber();
        String code = req.getCode();
        if (StringUtils.isBlank(phoneNumber) || StringUtils.isBlank(code)) {
            throw new BusinessException(BusinessExceptionEnum.REQUESRT_INFO_IS_NULL);
        }

        Map<String, String> respnseMap = request4Openid(code);
        if (MapUtil.isEmpty(respnseMap)) {
            throw new BusinessException(BusinessExceptionEnum.WX_REQUEST_BY_CODE_FAIL);
        }
        // String wxSessionKey = respnseMap.get("session_key");
        String wxOpenId = respnseMap.get("openid");
        List<UserDo> userDos = userMapper.selectUsers(UserDto.builder().phoneNumbers(Lists.newArrayList(phoneNumber)).build());
        String wxToken = String.valueOf(UUID.randomUUID());
        UserDo rspUserDo;
        if (CollectionUtils.isEmpty(userDos)) {
            // 没有该用户，新增一条
            UserDo userDo = UserDo.builder()
                    .wxToken(wxToken)
                    .phoneNumber(phoneNumber)
                    .wxOpenId(wxOpenId)
                    .build();
            rspUserDo = userDo;
            rspUserDo.setNickName("未设置昵称");
            userMapper.insertUser(userDo);
        } else {
            rspUserDo = userDos.get(0);
            rspUserDo.setWxToken(wxToken);
            rspUserDo.setWxOpenId(wxOpenId);
            String oriNickName = rspUserDo.getNickName();
            rspUserDo.setNickName(StringUtils.isBlank(oriNickName) ? "未设置昵称" : oriNickName);
            userMapper.updateUser(UpdateUserReq.builder()
                    .insertWxOpenId(wxOpenId)
                    .wxToken(wxToken)
                    .oriPhoneNumber(phoneNumber).build());
        }
        return rspUserDo;
    }

    private Map<String, String> request4Openid(String code) {
        WebClient webClient = WebClient.builder().defaultHeader("Content-Type", "application/json").build();
        Mono<String> mapMono = webClient.get().uri("https://api.weixin.qq.com/sns/jscode2session?appid={APPID}&secret={SECRET}&js_code={JSCODE}&grant_type=authorization_code", appId, appSecret, code).retrieve().bodyToMono(String.class);
        String response = mapMono.block();
        Map<String, String> respnseMap = JSONUtil.toBean(response, Map.class);
        if (!respnseMap.containsKey("session_key")) {
            log.error("请求微信登录信息失败：" + response);
            throw new BusinessException(BusinessExceptionEnum.LOGIN_ERR.getCode(), respnseMap.get("errmsg"));
        }
        return respnseMap;
    }

    /**
     * 通过token登录
     *
     * @param token 本系统token
     * @return 微信用户信息
     */
    @Override
    public UserDo loginWithToken(String token) {
        if (StringUtils.isBlank(token)) {
            throw new BusinessException(BusinessExceptionEnum.WX_TOKEN_LOGIN_FAIL);
        }
        List<UserDo> userDos = userMapper.selectUsers(UserDto.builder().wxToken(token)
                .build());
        if (CollectionUtils.isEmpty(userDos)) {
            throw new BusinessException(BusinessExceptionEnum.WX_TOKEN_LOGIN_FAIL);
        }
        return userDos.get(0);
    }

    /**
     * 保存用户信息并登录
     *
     * @param loginRsp 登录请求体
     * @return 包含token的用户信息
     */
    @Override
    public UserDo saveAndLogin(WxLoginReq loginRsp) {
        checkParam(loginRsp);
        String token = String.valueOf(UUID.randomUUID());
        String phoneNumber = loginRsp.getPhoneNumber();
        String wxOpenId = loginRsp.getWxOpenId();
        UserDo userDo = UserDo.builder()
                .wxOpenId(wxOpenId)
                .phoneNumber(phoneNumber)
                .wxToken(token)
                .build();

        List<UserDo> userDos = userMapper.selectUsers(UserDto.builder().phoneNumbers(Lists.newArrayList(phoneNumber)).build());
        if (CollectionUtils.isEmpty(userDos)) {
            userMapper.insertUser(userDo);
        } else {
            userMapper.updateUser(UpdateUserReq.builder()
                    .insertWxOpenId(wxOpenId)
                    .phoneNumber(phoneNumber)
                    .wxToken(token)
                    .build());
        }
        return userDo;
    }

    private static void checkParam(WxLoginReq loginRsp) {
        if (Objects.isNull(loginRsp)) {
            throw new BusinessException(BusinessExceptionEnum.REQUESRT_INFO_IS_NULL);
        }

        if (StringUtils.isBlank(loginRsp.getWxOpenId())) {
            throw new BusinessException(BusinessExceptionEnum.WX_OPENID_IS_NULL);
        }

        if (StringUtils.isBlank(loginRsp.getPhoneNumber())) {
            throw new BusinessException(BusinessExceptionEnum.WX_OPENID_IS_NULL);
        }
    }
}
